#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdbool.h>

extern bool enable_udp;
extern struct sockaddr_in u_server_addr;

void ascii_create_udp_header(char *buf, uint32_t req_id) {
	*buf++ = req_id >> 0x8;
	*buf++ = req_id & 0xff;
	*buf++ = 0;
	*buf++ = 0;
	*buf++ = 0;
	*buf++ = 1;

	*buf++ = 0;
	*buf++ = 0;
}

void ascii_parse_udp_header(char *recv_buf) {
	uint16_t req_id = htons(((uint16_t *)recv_buf)[0]);
	uint16_t seq_no = htons(((uint16_t *)recv_buf)[1]);
	uint16_t n_parts = htons(((uint16_t *)recv_buf)[2]);
	uint16_t reserved = htons(((uint16_t *)recv_buf)[3]);
	fprintf(stderr, "ascii reply udp_header req_id=%u seq_id=%u n_parts=%u reserved=%u\n",
			req_id, seq_no, n_parts, reserved);
}

/* set <key> <flags> <exptime> <bytes>\r\n */
void ascii_set_request(int sfd, char *key, char *value, int v_len) {
	char cmd_buf[128];
	int udp_prefix = 0;
	if (enable_udp) {
		ascii_create_udp_header(cmd_buf, 17);
		udp_prefix = 8;
	}
	size_t l = snprintf(NULL, 0, "set %s 0 0 %d\r\n", key, v_len);
	sprintf(cmd_buf + udp_prefix, "set %s 0 0 %d\r\n", key, v_len);
	sprintf(cmd_buf + udp_prefix + l, "%s\r\n", value);
	l += (udp_prefix+v_len+2);
	fprintf(stderr, "request(l=%zd) %s\n", l, cmd_buf + udp_prefix);
	fflush(stderr);

	ssize_t bytes_written;
	if (enable_udp) {
		bytes_written = sendto(sfd, cmd_buf, l, 0, (struct sockaddr *) &u_server_addr, sizeof(u_server_addr));
	} else {
		bytes_written = write(sfd, cmd_buf, l);
	}
	if (bytes_written < l) {
		fprintf(stderr, "ascii_set_request bytes_written=%zd < expected_len=%zd\n", bytes_written, l);
		exit(EXIT_FAILURE);
	}
}

void ascii_get_request(int sfd, char **keys, int num_key) {
	char cmd_buf[256];
	int udp_prefix = 0;
	if (enable_udp) {
		ascii_create_udp_header(cmd_buf, 33);
		udp_prefix = 8;
	}
	size_t l = (size_t)udp_prefix + 4; //udp_header + "gets"
	sprintf(cmd_buf + udp_prefix, "gets");
	for (int i=0; i<num_key; i++) {
		sprintf(cmd_buf + l, " %s", keys[i]);
		l += snprintf(NULL, 0, " %s", keys[i]);
	}
	sprintf(cmd_buf + l, "\r\n");
	l += 2;
	fprintf(stderr, "request(l=%zd) %s\n", l, cmd_buf + udp_prefix); //skip udp_header

	ssize_t bytes_written;
	if (enable_udp) {
		bytes_written = sendto(sfd, cmd_buf, l, 0, (struct sockaddr *) &u_server_addr, sizeof(u_server_addr));
	} else {
		bytes_written = write(sfd, cmd_buf, l);
	}
	if (bytes_written < l) {
		fprintf(stderr, "ascii_set_request bytes_written=%zd < expected_len=%zd\n", bytes_written, l);
		exit(EXIT_FAILURE);
	}
}

void ascii_parse_set_response(int sfd) {
	char reply_buf[128];
	char *p_buf;
	socklen_t s_l = sizeof(u_server_addr);
	ssize_t len;
	if (enable_udp) {
		len = recvfrom(sfd, reply_buf, 128, 0, (struct sockaddr *) &u_server_addr, &s_l);
	} else {
		len = read(sfd, reply_buf, 128);
		//reply_buf[64] = '\0';
		fprintf(stderr, "read len=%zd\n", len);
	}
	if (len > 0) {
		if (enable_udp) {
			ascii_parse_udp_header(reply_buf);
			p_buf = reply_buf + 8; //skip udp header
		} else {
			p_buf = reply_buf;
		}
		if (!strncmp(p_buf, "STORED", 6)) {
			fprintf(stderr, "stored\n");
		} else if (!strncmp(p_buf, "NOT_STORED", 10)) {
			fprintf(stderr, "not_stored\n");
		} else {
			fprintf(stderr, "\n ======== \n");
			for (int i=0; i<len; i++) {
				fprintf(stderr, "%c", p_buf[i]);
			}
			fprintf(stderr, "\n ======== \n");
		}
	}
}

void ascii_parse_get_response(int sfd) {
	char reply_buf[512];
	char *p_buf;
	socklen_t s_l = sizeof(u_server_addr);
	ssize_t len;
	if (enable_udp) {
		len = recvfrom(sfd, reply_buf, 128, 0, (struct sockaddr *)&u_server_addr, &s_l);
	} else {
		len = read(sfd, reply_buf, 512);
		//reply_buf[127] = '\0';
	}
	if (len > 0) {
		if (enable_udp) {
			ascii_parse_udp_header(reply_buf);
			p_buf = reply_buf + 8;
		} else {
			p_buf = reply_buf;
		}
		if (!strncmp(p_buf, "END", 3)) {
			fprintf(stderr, "miss get key\n");
		} else if (!strncmp(p_buf, "VALUE", 5)) {
			fprintf(stderr, "\n ======== \n");
			for (int i=0; i<len; i++) {
				fprintf(stderr, "%c", p_buf[i]);
			}
			fprintf(stderr, "\n ======== \n");
		}
	}
}

